import subprocess
import re
import threading
import time
import math
import sys

from apscheduler.triggers.cron import CronTrigger

from tools.locker import locker
from inspect import isfunction
from tools.auto_property import prop
from tools.scheduler import MyScheduler
from webapp.config import Config

__regex = re.compile(r"^[^/]+://\s*([^/]+)(/.*)?$", re.I)


def ping(url):
    """
    获取与指定url之间的网络延迟(毫秒)
    :return: int
    """
    if not isinstance(url, str):
        raise TypeError("url必须是字符串")
    url = url.strip()
    match = __regex.match(url)

    if match:
        url = match.group(1)

    begin = time.time()
    platform = sys.platform.lower()

    if platform == "win32":
        (code, _) = subprocess.getstatusoutput("ping -n 1 " + url)
    else:
        (code, _) = subprocess.getstatusoutput("ping -c 1 " + url)

    if code != 0:
        return -1

    end = time.time()
    return math.ceil((end - begin) * 1000)


class PingTask(object):
    interval = prop("interval", bind_field="_interval", default=1, check_type=(int, float))
    counter = prop("counter", bind_field="_counter", default=-1, check_type=(int, float))
    status = prop("status", bind_field="__status__", readonly=True)  # 0停止 1运行 2暂停

    def __init__(self, action, callback=None):
        if not isfunction(action):
            raise TypeError("必须提供 action 函数")
        self.__callback__ = None
        if isfunction(callback):
            self.__callback__ = callback
        elif callback is not None:
            raise TypeError("参数 callback 必须为函数")
        self.__action__ = action
        self.__status__ = 0  # 0停止 1运行 2暂停
        self.__locker__ = locker()
        self.__thread__ = None
        self._interval = Config.PING_INTERVAL or 1
        self._counter = Config.PING_COUNTER or -1

    def pause(self):
        """
        暂停
        """
        with self.__locker__.lock(self.interval * 2):
            if self.__status__ == 0:
                raise RuntimeError("任务当前为停止状态")
            if self.__status__ == 1:
                self.__status__ = 2

    def resume(self):
        """
        暂停后恢复
        """
        with self.__locker__.lock(self.interval * 2):
            if self.__status__ == 0:
                raise RuntimeError("任务当前为停止状态")
            if self.__status__ == 2:
                self.__status__ = 1

    def stop(self):
        """
        停止
        """
        with self.__locker__.lock(self.interval * 2):
            self.__status__ = 0
        while self.__thread__ is not None:
            time.sleep(1)

    def __thread_run__(self):
        """
        线程任务
        """
        counter = self.counter
        try:
            while True:
                if self.__status__ == 2:
                    time.sleep(self.interval)
                    continue
                elif self.__status__ == 0:
                    return
                result = self.__action__()
                if isfunction(self.__callback__):
                    self.__callback__(result)
                if counter > 0:
                    counter -= 1
                if counter == 0:
                    break
                time.sleep(self.interval)
        except BaseException as ex:
            print("出现错误:" + repr(ex))
        finally:
            with self.__locker__.lock(self.interval * 2):
                self.__status__ = 0
                self.__thread__ = None

    def is_starting(self):
        """
        是否已经启动
        :return: Boolean
        """
        return self.__status__ in (1, 2)

    def is_paused(self):
        """
        是否暂停中
        :return:
        """
        return self.__status__ == 2

    def run(self):
        with self.__locker__.lock(self.interval * 2):
            if self.__status__ == 0:
                self.__status__ = 1
                self.__thread__ = threading.Thread(target=self.__thread_run__)
                self.__thread__.start()


def keep_ping():
    from tools.linq import linq
    import webview

    colors = [
        {"ping": -1, "color": "#898989"},
        {"ping": 100, "color": "#199c09"},
        {"ping": 500, "color": "#eae100"},
        {"ping": 1000, "color": "#fa7a06"},
        {"ping": float("inf"), "color": "red"},
    ]

    interval = Config.PING_INTERVAL or 1
    url = Config.PING_URL or "www.baidu.com"

    def ping_action():
        p = ping(url)
        webview.evaluate_js(
            """
            vm.common.networkState({
              state:'%s',
              fps: '%s ms'
            })
            """
            % (linq(colors).where(lambda x: p <= x["ping"]).first()["color"], str(p))
        )

    trigger = CronTrigger(second='*/%d' % interval)
    MyScheduler.backgroundScheduler.add_job(ping_action, trigger, id="ping", max_instances=1)


if __name__ == "__main__":
    print("ping 127.0.0.1 延迟 %s 毫秒" % ping("127.0.0.1"))
    print("ping www.baidu.com 延迟 %s 毫秒" % ping("www.baidu.com"))
    print("ping http://www.sina.com/xxxx 延迟 %s 毫秒" % ping("http://www.sina.com/xxxx"))

    print("启动任务 10 次, ping www.baidu.com")
    task = PingTask(lambda: print("延迟 %s 毫秒" % ping("www.baidu.com")))
    task.counter = 10
    task.interval = 1
    task.run()
    while task.is_starting():
        time.sleep(1)
    print("任务已停止")
